package com.pig4cloud.pigx.user.wallet;

import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pig4cloud.pigx.user.constant.WalletConstant;
import com.pig4cloud.pigx.user.entity.Currency;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.core.contract.Trc20Contract;
import org.tron.trident.proto.Response;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 */
@Component
@Slf4j
@AllArgsConstructor
//public class TestRunner implements ApplicationRunner {
public class TestRunner {

	private final static String ETH = "ETH";
	private final static String BTC = "BTC";
	private final static String TRX = "TRX";
	private final static String USDT = "USDT";

	private final static String ETH_SMART_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7";

	private final static String TRX_SMART_ADDRESS = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";

	private final Web3j web3j = Web3j.build(new HttpService("https://mainnet.infura.io/v3/3126934b551242d1b3f041c5e9fbfb0a"));

	public void run(ApplicationArguments args) throws IOException {
		List<Currency> list = new Currency().selectList(new QueryWrapper<Currency>() {{
			ne("name", WalletConstant.USDT);
			isNull("last_price");
		}});
		// 筛选币种
		List<Currency> eth = list.stream().filter(e -> e.getName().equals(ETH)).collect(Collectors.toList());

		List<Currency> btc = list.stream().filter(e -> e.getName().equals(BTC)).collect(Collectors.toList());

		List<Currency> trx = list.stream().filter(e -> e.getName().equals(TRX)).collect(Collectors.toList());
		// 将usdt按地址划分到eth和trx里 并赋予智能合约地址
		List<Currency> usd = list.stream().filter(e -> e.getName().equals(USDT)).collect(Collectors.toList());
		for (Currency currency : usd) {
			if ("T".equals(currency.getNodeAddress().substring(0,1))) {
				currency.setSmartContractAddress(TRX_SMART_ADDRESS);
				trx.add(currency);
			}
			if ("0x".equals(currency.getNodeAddress().substring(0,2))) {
				currency.setSmartContractAddress(ETH_SMART_ADDRESS);
				eth.add(currency);
			}
		}
		for (Currency currency : trx) {
			if (currency.getName().equals(USDT)) {
				BigDecimal balance = this.getTronUsdBalance(currency.getNodeAddress());
				currency.setLastPrice(balance);
				currency.updateById();
			}
			if (currency.getName().equals(TRX)) {
				BigDecimal balance = this.getTrxBalance(currency.getNodeAddress());
				currency.setLastPrice(balance);
				currency.updateById();
			}
		}
		for (Currency currency : eth) {
			if (currency.getName().equals(USDT)) {
				BigDecimal balance = this.getEthUsdBalance(currency.getNodeAddress(),ETH_SMART_ADDRESS);
				currency.setLastPrice(balance);
				currency.updateById();
			}
			if (currency.getName().equals(ETH)) {
				BigDecimal balance = this.getEthBalance(currency.getNodeAddress());
				currency.setLastPrice(balance);
				currency.updateById();
			}
		}
	}

	private BigDecimal getEthBalance(String address){
		try {
			EthGetBalance send = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
			return Convert.fromWei(new BigDecimal(send.getBalance()), Convert.Unit.ETHER);
		} catch (Exception e) {
			return null;
		}
	}

	private BigDecimal getTrxBalance(String address) {
		try {
			Response.Account account = ApiWrapper.ofMainnet("8e812436a0e3323166e1f0e8ba79e19e217b2c4a53c970d4cca0cfb1078979df",
					"f39d7270-6c80-4389-9962-a09eefff8e38").getAccount(address);
			return BigDecimal.valueOf(account.getBalance()).divide(new BigDecimal("1000000"),6, RoundingMode.DOWN);
		} catch (Exception e) {
			return null;
		}
	}

	public BigDecimal getEthUsdBalance(String address,String smartAddress) {
		try {
			// 出口参数
			List<TypeReference<?>> outputParameters = new ArrayList<>();
			TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
			};
			outputParameters.add(typeReference);
			String methodName = "balanceOf";
			// 入口参数
			List<Type> inputParameters = new ArrayList<>();

			// 用户账户地址
			Address userAddress = new Address(address);
			inputParameters.add(userAddress);
			// 获取ETH余额
			Function function = new Function(methodName, inputParameters, outputParameters);
			String data = FunctionEncoder.encode(function);
			Request<?, EthCall> ethCallRequest = web3j.ethCall(
					Transaction.createEthCallTransaction(
							// 地址（账户）
							address,
							// 智能合约地址（币种列表）
							smartAddress,
							data
					),
					DefaultBlockParameterName.LATEST);
			EthCall send;
			try {
				send = ethCallRequest.send();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}

			String valueStr = new BigInteger(send.getValue().substring(2), 16).toString();
			// 保留币种列表中该币种指定的精度，转化成String类型数据
			String balance = NumberUtil.toBigDecimal(valueStr).movePointLeft(8).stripTrailingZeros().toPlainString();
			return NumberUtil.toBigDecimal(balance);
		} catch (Exception e) {
			return null;
		}
	}

	private BigDecimal getTronUsdBalance(String address) {
		try {
			ApiWrapper wrapper = ApiWrapper.ofMainnet("8e812436a0e3323166e1f0e8ba79e19e217b2c4a53c970d4cca0cfb1078979df",
					"f39d7270-6c80-4389-9962-a09eefff8e38");
			org.tron.trident.core.contract.Contract contract = wrapper.getContract(TRX_SMART_ADDRESS);
			// TRON余额查询
			Trc20Contract trc20Contract = new Trc20Contract(contract, address, wrapper);
			BigInteger bigInteger = trc20Contract.balanceOf(address);
			String balance = NumberUtil.toBigDecimal(String.valueOf(bigInteger))
					.movePointLeft(6)
					.stripTrailingZeros()
					.toPlainString();
			return NumberUtil.toBigDecimal(balance);
		} catch (Exception e) {
			return null;
		}
	}


}
